/*
 *  iFreeOS kernel
 *  Copyright (C) 2002 iFreeOS Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * COPYRIGHT:        See COPYING in the top level directory
 * PROJECT:          iFreeOS kernel
 * FILE:             drivers/filesystem/ntfs/mft.c
 * PURPOSE:          NTFS filesystem driver
 * PROGRAMMER:       Eric Kohl
 *                   Updated by Valentin Verkhovsky  2003/09/12
 */

/* INCLUDES *****************************************************************/

#include "ntfs.h"

#define NDEBUG
#include <debug.h>

/* GLOBALS *****************************************************************/


/* FUNCTIONS ****************************************************************/


NTSTATUS
NtfsOpenMft(PDEVICE_EXTENSION Vcb)
{
//    PVOID Bitmap;
    PFILE_RECORD_HEADER MftRecord;
    PFILE_RECORD_HEADER FileRecord;
//    PATTRIBUTE Attribute;
//    PATTRIBUTE AttrData;
//    PRESIDENT_ATTRIBUTE ResAttr;

    NTSTATUS Status;
    ULONG BytesPerFileRecord;
    ULONG n;
    ULONG i;

    DPRINT1("NtfsOpenMft() called\n");

    BytesPerFileRecord = Vcb->NtfsInfo.BytesPerFileRecord;

    MftRecord = ExAllocatePoolWithTag(NonPagedPool,
                                      BytesPerFileRecord,
                                      TAG_NTFS);
    if (MftRecord == NULL)
    {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    Status = NtfsReadSectors(Vcb->StorageDevice,
                             Vcb->NtfsInfo.MftStart.u.LowPart * Vcb->NtfsInfo.SectorsPerCluster,
                             BytesPerFileRecord / Vcb->NtfsInfo.BytesPerSector,
                             Vcb->NtfsInfo.BytesPerSector,
                             (PVOID)MftRecord,
                             FALSE);
    if (!NT_SUCCESS(Status))
    {
        ExFreePool(MftRecord);
        return Status;
    }

    FixupUpdateSequenceArray(MftRecord);

//    Attribute = FindAttribute(MftRecord, AttributeBitmap, 0);

    /* Get number of file records*/
    n = AttributeDataLength(FindAttribute(MftRecord, AttributeData, 0)) / BytesPerFileRecord;

    FileRecord = ExAllocatePoolWithTag(NonPagedPool,
                                       BytesPerFileRecord,
                                       TAG_NTFS);
    if (FileRecord == NULL)
    {
        ExFreePool(MftRecord);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    /* Enumerate MFT Records */
    DPRINT("Enumerate MFT records\n");
    for (i = 0; i < n; i++)
    {
        ReadFileRecord(Vcb,
                       i,
                       FileRecord,
                       MftRecord);

        if (FileRecord->Ntfs.Type == NRH_FILE_TYPE &&
            (FileRecord->Flags & FRH_IN_USE))
        {
            DPRINT("\nFile  %lu\n\n", i);

            /* Enumerate attributtes */
            NtfsDumpFileAttributes (FileRecord);
            DbgPrint("\n\n");
        }
    }

    ExFreePool(FileRecord);
    ExFreePool(MftRecord);

    return Status;
}


PATTRIBUTE
FindAttribute(PFILE_RECORD_HEADER FileRecord,
              ATTRIBUTE_TYPE Type,
              PWSTR name)
{
    PATTRIBUTE Attribute;

    UNREFERENCED_PARAMETER(name);

    Attribute = (PATTRIBUTE)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
    while (Attribute < (PATTRIBUTE)((ULONG_PTR)FileRecord + FileRecord->BytesInUse) &&
           Attribute->AttributeType != (ATTRIBUTE_TYPE)-1)
    {
        if (Attribute->AttributeType == Type)
        {
            return Attribute;
        }

        Attribute = (PATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Length);
    }

    return NULL;
}


ULONG
AttributeAllocatedLength(PATTRIBUTE Attribute)
{
    if (Attribute->Nonresident)
    {
        return ((PNONRESIDENT_ATTRIBUTE)Attribute)->AllocatedSize;
    }

    return ((PRESIDENT_ATTRIBUTE)Attribute)->ValueLength;
}


ULONG
AttributeDataLength(PATTRIBUTE Attribute)
{
    if (Attribute->Nonresident)
    {
        return ((PNONRESIDENT_ATTRIBUTE)Attribute)->DataSize;
    }

    return ((PRESIDENT_ATTRIBUTE)Attribute)->ValueLength;
}


VOID
ReadAttribute(PATTRIBUTE attr,
              PVOID buffer,
              PDEVICE_EXTENSION Vcb,
              PDEVICE_OBJECT DeviceObject)
{
    PNONRESIDENT_ATTRIBUTE NresAttr = (PNONRESIDENT_ATTRIBUTE)attr;

    UNREFERENCED_PARAMETER(DeviceObject);

    if (attr->Nonresident == FALSE)
    {
        memcpy(buffer,
               (PVOID)((ULONG_PTR)attr + ((PRESIDENT_ATTRIBUTE)attr)->ValueOffset),
               ((PRESIDENT_ATTRIBUTE)attr)->ValueLength);
    }

    ReadExternalAttribute(Vcb,
                          NresAttr,
                          0,
                          (ULONG)(NresAttr->LastVcn) + 1,
                          buffer);
}


NTSTATUS
ReadFileRecord(PDEVICE_EXTENSION Vcb,
               ULONG index,
               PFILE_RECORD_HEADER file,
               PFILE_RECORD_HEADER Mft)
{
    PVOID p;
    ULONG BytesPerFileRecord = Vcb->NtfsInfo.BytesPerFileRecord;
    ULONG clusters = max(BytesPerFileRecord / Vcb->NtfsInfo.BytesPerCluster, 1);
    ULONGLONG vcn = index * BytesPerFileRecord / Vcb->NtfsInfo.BytesPerCluster;
    LONG m = (Vcb->NtfsInfo.BytesPerCluster / BytesPerFileRecord) - 1;
    ULONG n = m > 0 ? (index & m) : 0;

    p = ExAllocatePoolWithTag(NonPagedPool,
                              clusters * Vcb->NtfsInfo.BytesPerCluster,
                              TAG_NTFS);

    ReadVCN (Vcb, Mft, AttributeData, vcn, clusters, p);

    memcpy(file,
           (PVOID)((ULONG_PTR)p + n * BytesPerFileRecord),
           BytesPerFileRecord);

    ExFreePool(p);

    FixupUpdateSequenceArray(file);

    return STATUS_SUCCESS;
}


VOID
ReadExternalAttribute(PDEVICE_EXTENSION Vcb,
                      PNONRESIDENT_ATTRIBUTE NresAttr,
                      ULONGLONG vcn,
                      ULONG count,
                      PVOID buffer)
{
    ULONGLONG lcn;
    ULONGLONG runcount;
    ULONG readcount;
    ULONG left;
    ULONG n;

    PUCHAR bytes = (PUCHAR)buffer;

    for (left = count; left > 0; left -= readcount)
    {
        FindRun(NresAttr, vcn, &lcn, &runcount);

//        readcount = (ULONG)(__min(runcount, left));
        readcount = (ULONG)min(runcount, left);


        n = readcount * Vcb->NtfsInfo.BytesPerCluster;

        if (lcn == 0)
            memset(bytes, 0, n);
        else
            ReadLCN(Vcb, lcn, readcount, bytes);

        vcn += readcount;
        bytes += n;
    }
}


VOID
ReadVCN(PDEVICE_EXTENSION Vcb,
        PFILE_RECORD_HEADER file,
        ATTRIBUTE_TYPE type,
        ULONGLONG vcn,
        ULONG count,
        PVOID buffer)
{
    PNONRESIDENT_ATTRIBUTE NresAttr;
    PATTRIBUTE attr;

    attr = FindAttribute(file, type, 0);

    NresAttr = (PNONRESIDENT_ATTRIBUTE) attr;

    if (NresAttr == 0 || (vcn < NresAttr->StartVcn ||vcn > NresAttr->LastVcn))
    {
//      PATTRIBUTE attrList = FindAttribute(file,AttributeAttributeList,0);
        DbgPrint("Exeption \n");
//      KeDebugCheck(0);
    }

    ReadExternalAttribute(Vcb, NresAttr, vcn, count, buffer);
}


#if 0
BOOL bitset(PUCHAR bitmap, ULONG i)
{
    return (bitmap[i>>3] & (1 << (i & 7))) !=0;
}
#endif


VOID
FixupUpdateSequenceArray(PFILE_RECORD_HEADER file)
{
    PUSHORT usa = (PUSHORT)((ULONG_PTR)file + file->Ntfs.UsaOffset);
    PUSHORT sector = (PUSHORT)file;
    ULONG i;

    for (i = 1; i < file->Ntfs.UsaCount; i++)
    {
        sector[255] = usa[i];
        sector += 256;
    }
}


NTSTATUS
ReadLCN(PDEVICE_EXTENSION Vcb,
        ULONGLONG lcn,
        ULONG count,
        PVOID buffer)
{
    LARGE_INTEGER DiskSector;

    DiskSector.QuadPart = lcn;

    return NtfsReadSectors(Vcb->StorageDevice,
                           DiskSector.u.LowPart * Vcb->NtfsInfo.SectorsPerCluster,
                           count * Vcb->NtfsInfo.SectorsPerCluster,
                           Vcb->NtfsInfo.BytesPerSector,
                           buffer,
                           FALSE);
}

/* EOF */
